package ClassData.Visitor.MemoryMapper;

import java.util.Formatter;

import ClassData.AttributeInfo;
import ClassData.AttributeInfoLineNumberTable;
import ClassData.AttributeInfo_Code;
import ClassData.AttributeInfo_ConstantValue;
import ClassData.AttributeInfo_Deprecated;
import ClassData.AttributeInfo_Exceptions;
import ClassData.AttributeInfo_InnerClasses;
import ClassData.AttributeInfo_LocalVariableTable;
import ClassData.AttributeInfo_Signature;
import ClassData.AttributeInfo_SourceFile;
import ClassData.AttributeInfo_Synthetic;
import ClassData.ClassData;
import ClassData.ClassDataList;
import ClassData.ConstantPool;
import ClassData.ConstantPoolClass_Info;
import ClassData.ConstantPoolDouble_Info;
import ClassData.ConstantPoolField_Info;
import ClassData.ConstantPoolFloat_Info;
import ClassData.ConstantPoolInfo;
import ClassData.ConstantPoolInteger_Info;
import ClassData.ConstantPoolLongValueDummy_Info;
import ClassData.ConstantPoolLong_Info;
import ClassData.ConstantPoolMethod_Info;
import ClassData.ConstantPoolName_Info;
import ClassData.ConstantPoolString_Info;
import ClassData.ConstantPoolUtf8_Info;
import ClassData.ExceptionHandler;
import ClassData.FieldInfo;
import ClassData.InterfaceInfo;
import ClassData.MethodInfo;
import DataFile.DataFileCreator;
import DataFile.Structure_Template;

/**
 * 
 * @author Domnik Ollesz
 * @version 0.1
 */
public class MemoryMapper extends AbstractVisitor {

	public static final int ADRESS_WIDTH     = 4;
	public static final int SIZE_FIELD_WIDTH = 4;
	public static final int SHORT_WIDTH      = 2;
	public static final int BYTE_WIDTH       = 1;
	public static final int INTEGER_WIDTH    = 4;
	public static final int LONG_WIDTH       = 8;
	public static final int DOUBLE_WIDTH     = 8;
	public static final int FLOAT_WIDTH      = INTEGER_WIDTH;
	
	private int baseaddress;
	private int currentAddress;
		
	public MemoryMapper(int baseaddress, String path) {
		super(path);
		this.baseaddress    = baseaddress;
		this.currentAddress = baseaddress;
	}
	
	/**
	 * Performs memory mapping for given list of DataClass elements.
	 * @param dataList List with DataClass elements.
	 */
	public void doMapping(ClassDataList dataList) {
		visit_ClassDataList(dataList);
		close();
	}
	
	// functionality goes here:
	
	@Override
	public void visit_AttributeInfo(AttributeInfo element) {
		
	}

	@Override
	public void visit_AttributeInfo_Code(AttributeInfo_Code element) {
		
	}

	@Override
	public void visit_AttributeInfo_ConstantValue(AttributeInfo_ConstantValue element) {
		
	}

	@Override
	public void visit_AttributeInfo_Deprecated(AttributeInfo_Deprecated element) {

	}

	@Override
	public void visit_AttributeInfo_Excetions(AttributeInfo_Exceptions element) {
		
	}

	@Override
	public void visit_AttributeInfo_InnerClass(AttributeInfo_InnerClasses element) {
		
	}

	@Override
	public void visit_AttributeInfo_LocalVariableTable(AttributeInfo_LocalVariableTable element) {
		
	}

	@Override
	public void visit_AttributeInfo_Signature(AttributeInfo_Signature element) {
		
	}

	@Override
	public void visit_AttributeInfo_SourceFile(AttributeInfo_SourceFile element) {
		
	}

	@Override
	public void visit_AttributeInfo_Synthetic(AttributeInfo_Synthetic element) {
		
	}

	@Override
	public void visit_AttributeLineNumberTable(AttributeInfoLineNumberTable element) {
		
	}

	@Override
	public void visit_ClassData(ClassData classData) {
		
		// resize BaseAddress
		baseaddress = resizeMemory(baseaddress, 2, 0);
		
		// ConstantPool
		write("\nClass " + classData.getClassName() + "\n");
		classData.getConstantPool().receive_Visitor(this);
		
		// Methods
		write("\nClass " + classData.getClassName() + 
				"\nMethods: " + classData.getMethodsCount()+"\n");
		int i = 1;
		for (MethodInfo method : classData.getMethods()) {
			write((new Formatter().format("  %03d ",i++)).toString());
			method.receive_Visitor(this);
		}
	}

	@Override
	public void visit_ClassDataList(ClassDataList classDataList) {
		
		// resize BaseAddress
		System.out.println("BaseAddress: ClassDataList - " 
				+ new Formatter().format("0x%8x",baseaddress));
		baseaddress = resizeMemory(baseaddress, 1, classDataList.size());
		
		// visit classes 
		for (ClassData classdata : classDataList) {
			visit_ClassData(classdata);
		}
		
	}

	@Override
	public void visit_ConstantPool(ConstantPool constantPool) {

		write("\nConstantPool: " + (constantPool.size()) + " elements\n");
		
		for(int i = 1; i < constantPool.size(); i++) {
			write((new Formatter().format("  %03d ",i)).toString());
			switch(constantPool.get(i).getType()) {
				case ConstantPoolInfo.CONSTANT_Class : 
					visit_ConstantPoolClass_Info((ConstantPoolClass_Info)(constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_Double: 
					visit_ConstantPoolDouble_Info((ConstantPoolDouble_Info)(constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_Fieldref:
					visit_ConstantPoolField_Info((ConstantPoolField_Info)(constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_Float:
					visit_ConstantPoolFloat_Info((ConstantPoolFloat_Info)(constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_Integer:
					visit_ConstantPoolInteger_Info((ConstantPoolInteger_Info)(constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_InterfaceMethodref:
					// not implemented yet
					break;
				case ConstantPoolInfo.CONSTANT_Long:
					// visit_ConstantPoolLongValueDummy_Info((ConstantPoolLongValueDummy_Info) (constantPool.get(1)));break;
					break;
				case ConstantPoolInfo.CONSTANT_Methodref:
					visit_ConstantPoolMethod_Info((ConstantPoolMethod_Info) (constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_NameAndType:
					visit_ConstantPoolName_info((ConstantPoolName_Info)(constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_String:
					visit_ConstantPoolString_Info((ConstantPoolString_Info) (constantPool.get(i)));
					break;
				case ConstantPoolInfo.CONSTANT_Utf8:
					visit_ConstantPoolUtf8_Info((ConstantPoolUtf8_Info) (constantPool.get(i)));
			}
		}
	}

	@Override
	public void visit_ConstantPoolClass_Info(ConstantPoolClass_Info cpClassInfo) {
		write("CP ClassInfo: " + cpClassInfo.GetNameIndex() + "name index\n");
	}

	@Override
	public void visit_ConstantPoolDouble_Info(ConstantPoolDouble_Info element) {
		write("CP DoubleInfo:\n");
	}

	@Override
	public void visit_ConstantPoolField_Info(ConstantPoolField_Info element) {
		write("CP FieldInfo:\n");
	}

	@Override
	public void visit_ConstantPoolFloat_Info(ConstantPoolFloat_Info element) {
		write("CP FloatInfo:\n");
	}

	@Override
	public void visit_ConstantPoolInfo(ConstantPoolInfo element) {
		write("CP Info:\n");
	}

	@Override
	public void visit_ConstantPoolInteger_Info(ConstantPoolInteger_Info element) {
		write("CP IntegerInfo\n");
	}

	@Override
	public void visit_ConstantPoolLongValueDummy_Info(
			ConstantPoolLongValueDummy_Info element) {
		
	}

	@Override
	public void visit_ConstantPoolLong_Info(ConstantPoolLong_Info element) {
		write("CP LongInfo\n");
	}

	@Override
	public void visit_ConstantPoolMethod_Info(ConstantPoolMethod_Info element) {
		write("CP MethodInfo\n");
	}

	@Override
	public void visit_ConstantPoolName_info(ConstantPoolName_Info element) {
		write("CP NameInfo\n");
	}

	@Override
	public void visit_ConstantPoolString_Info(ConstantPoolString_Info element) {
		write("CP StringInfo\n");
	}

	@Override
	public void visit_ConstantPoolUtf8_Info(ConstantPoolUtf8_Info element) {
		write("CP Utf8Info\n");
	}

	@Override
	public void visit_ExceptionHandler(ExceptionHandler element) {
		
	}

	@Override
	public void visit_FieldInfo(FieldInfo element) {
		
	}

	@Override
	public void visit_InterfaceInfo(InterfaceInfo element) {
		
	}

	@Override
	public void visit_MethodInfo(MethodInfo methodInfo) {
		write("Method:" + methodInfo.getMethodName()+"\n");
	}

	private int resizeMemory(int baseAddress, int type, int n) {
		switch (type) {
			case 1: // CLASS DATA LIST
				System.out.println("" + new Formatter().format("0x%8x",baseaddress) + " ClassDataList");
				baseAddress += SIZE_FIELD_WIDTH; //
				baseAddress += SIZE_FIELD_WIDTH; //
				baseAddress += n * ADRESS_WIDTH; // 
				break;
			case 2:
				System.out.println("" + new Formatter().format("0x%8x",baseaddress) + " ClassData");
				baseAddress +=ADRESS_WIDTH; // constant pool table address
				baseAddress +=ADRESS_WIDTH; // Field table address
				baseAddress +=ADRESS_WIDTH; // Method table address
			
				baseAddress +=SHORT_WIDTH; // Static data size
				baseAddress +=SHORT_WIDTH; // Dynamic data size
				baseAddress +=SHORT_WIDTH; // Static Data Handle;
				baseAddress +=SHORT_WIDTH; // unused
				break;
			default:
				return baseAddress;
		}
		return baseAddress;
	}
	
	class MethodExceptions {
		private int clazz;
		private short[] exceptions;
		public MethodExceptions(int clazz, short[] exceptions) {
			this.clazz=clazz;
			this.exceptions=exceptions;
		}
		public int getClazz() {
			return clazz;
		}
		public short[] getExceptions() {
			return exceptions;
		}
	}
	
	MethodExceptions classExceptions[];
	
}

